
#import "SlideShowView.h"
#import "Thinker.h"
#import <appkit/TextField.h>
#import <appkit/Slider.h>
#import <appkit/Button.h>
#import <appkit/NXImage.h>
#import <math.h>
#import <libc.h>
#import <dpsclient/wraps.h>
#import <appkit/OpenPanel.h>
#import <appkit/Panel.h>
#import <appkit/publicWraps.h>
#import <c.h>
#import <defaults/defaults.h>
#import <sys/file.h>

@implementation SlideShowView

#define	DOBOUNCE	"ssSlideShowDoBounce"	
#define	SECSPERSLIDE	"ssSlideShowSecsPerSlide"
#define	SLIDESPATH	"ssSlideShowSlidesFile"
#define	FILEEXTENSION	"anim"

// x & y periods in milliseconds
#define X_PERIOD 20000.0
#define Y_PERIOD 19000.0

#define PI 3.1415926535

#define SCREEN_WIDTH 1120
#define SCREEN_HEIGHT 832

#define MAX_IMAGE_WIDTH 1120
#define MAX_IMAGE_HEIGHT 832

#define MAX_X_SPEED (26)
#define MAX_Y_SPEED (26)

#define BUFFER_WIDTH (MAX_IMAGE_WIDTH + MAX_X_SPEED + 1)
#define BUFFER_HEIGHT (MAX_IMAGE_HEIGHT + MAX_Y_SPEED + 1)



// do the next image if the time is right
- oneStep
{
	NXRect black = {0,0,0,0};
	NXRect imageRect;
	BRECT new;
	
	then = now;
	now = currentTimeInMs();

	/* animate by selecting a new image to blit */
	[self incrementImageNumber];
	
	if ( bounce ) {
		// calculate new image x & y position
		xpos = ((1 + sin(((float)now) / X_PERIOD * 2. * PI))/2.0) 
			* maxCoord.x;
		ypos = ((1 + sin(((float)now) / Y_PERIOD * 2. * PI))/2.0) 
			* maxCoord.y;
		
		
		if (xpos < (old.l - MAX_X_SPEED)) xpos = old.l - MAX_X_SPEED;
		else if (xpos > (old.l + MAX_X_SPEED)) xpos = old.l + MAX_X_SPEED;
	
		if (ypos < (old.b - MAX_Y_SPEED)) ypos = old.b - MAX_Y_SPEED;
		else if (ypos > (old.b + MAX_Y_SPEED)) ypos = old.b + MAX_Y_SPEED;
	}
	else {
		NXRect	vRect;
		[self getVisibleRect:&vRect];
		xpos = ( vRect.size.width - currentImageSize.width ) / 2;
		ypos = ( vRect.size.height - currentImageSize.height ) / 2;
	}
	
	/* animate new image to blit */
	new.l = floor(xpos);
	new.b = floor(ypos);
	new.r = new.l + currentImageSize.width;
	new.t = new.b + currentImageSize.height;
	
	imageRect.origin.x = 0;
	imageRect.origin.y = 0;
	imageRect.size.width = currentImageSize.width;
	imageRect.size.height = currentImageSize.height;
	
	redrawTo.x = MIN(new.l, old.l);
	redrawTo.y = MIN(new.b, old.b);

	redraw.origin.x = 0;
	redraw.origin.y = 0;
	redraw.size.width = (MAX(new.r, old.r)) - redrawTo.x + 1;
	redraw.size.height = (MAX(new.t, old.t)) - redrawTo.y + 1;
	
	black.size= redraw.size;

	imageTo.x = new.l - redrawTo.x;
	imageTo.y = new.b - redrawTo.y;

	[buffer lockFocus];
	
	PSsetgray(0);
	NXRectFill(&black);
	[currentImage composite:NX_SOVER fromRect:&imageRect toPoint:&imageTo];

	[buffer unlockFocus];
		
	// Now bring it onto the screen
	
	[buffer composite:NX_COPY fromRect:&redraw toPoint:&redrawTo];

	old = new;

	return self;
}



static BOOL noSSSlidesFile = FALSE;

- initFrame:(const NXRect *)frameRect
{
	const char	*ssSlideShowDoBounce,
			*ssSlideShowSecsPerSlide,
			*slidesPath;
	static char	moduleDirectory[MAXPATHLEN];
		

	NXRect		black = {0, 0, BUFFER_WIDTH, BUFFER_HEIGHT };

	[super initFrame:frameRect];
	[self allocateGState];		// For faster lock/unlockFocus
	[self setClipping:NO];		// even faster...

	//in this case, I only need one buffer for several Views
	if (!(buffer = [NXImage findImageNamed:"worldBuffer"]))
	{
		buffer = [[NXImage alloc] initSize:&black.size];
		[buffer setName:"worldBuffer"];
	}
	
	if ([buffer lockFocus])
	{
		PSsetgray(0);
		NXRectFill(&black);
		[buffer unlockFocus];
	}


	ssSlideShowDoBounce = NXGetDefaultValue([NXApp appName], DOBOUNCE);
	if (ssSlideShowDoBounce == NULL) bounce = YES;
	else { bounce = (!strcmp(ssSlideShowDoBounce,"1")) ? YES : NO; }
	
	ssSlideShowSecsPerSlide = NXGetDefaultValue([NXApp appName], SECSPERSLIDE);
	if (ssSlideShowSecsPerSlide == NULL) secsPerSlide = 5;
	else secsPerSlide = atoi(ssSlideShowSecsPerSlide);
	
	ssslidesPath = (char *)malloc(sizeof(char)*MAXPATHLEN);
	slidesPath = NXGetDefaultValue([NXApp appName], SLIDESPATH);
	if (slidesPath != NULL) strcpy(ssslidesPath, slidesPath);

	if ( (![self readSlidesFromFile:ssslidesPath]) || (slidesPath == NULL) )
	{
		sprintf(moduleDirectory,"%s/SlideShow.%s", [(BSThinker()) moduleDirectory:"SlideShow"], FILEEXTENSION);
		strcpy(ssslidesPath, moduleDirectory);
		[self readSlidesFromFile:ssslidesPath];
	}
	
	return self;
}

- readSlidesFromFile:(const char *)slidesPath
{
	int		i, f;
	id		local_image;
	NXSize		localImageSize,
			newMaxImageSize;
	id		newImageList;
	char		slideFrame[MAXPATHLEN];

	// construct the image list
	newImageList = [[List alloc] init];
	for (i=0; ; i++)
	{	
		sprintf(slideFrame, "%s/%d.tiff", slidesPath, i+1);
		if (!(local_image = [NXImage findImageNamed:slideFrame]))
		{
			if ((f=open(slideFrame, O_RDONLY)) < 0) break;
			close(f);

			local_image = [[NXImage alloc] initFromFile:slideFrame];
			if (local_image == NULL) break;	// never null, even if no file
			[local_image setName:slideFrame];
		}

		[newImageList addObject:local_image];
		
		[local_image getSize: &localImageSize];
		newMaxImageSize.width = MAX(newMaxImageSize.width, localImageSize.width);
		newMaxImageSize.height = MAX(newMaxImageSize.height, localImageSize.height);
	}

	if (i == 0)
	{
		if (!slidesPath) NXRunAlertPanel([NXApp appName], "Could not open %s", NULL, NULL, NULL, slidesPath);
		noSSSlidesFile = TRUE;
		NXBeep();
		[[pathTextField selectText:self] setStringValue:ssslidesPath];
		return nil;
	}
	else {
		maxImageSize = newMaxImageSize;
		numberOfFrames = i;
		currentFrame = 0;
	
		[[imageList freeObjects] free];
		imageList = newImageList;

		if (ssslidesPath != slidesPath) strcpy(ssslidesPath, slidesPath);
		[[pathTextField selectText:self] setStringValue:ssslidesPath];
		NXWriteDefault([NXApp appName], SLIDESPATH, ssslidesPath);
	}
	
	nextRotationTime = 0;
	[self newViewSize];

	return self;
}

- sizeTo:(NXCoord)width :(NXCoord)height
{
	[super sizeTo:width :height];
	[self newViewSize];
	return self;
}

- drawSelf:(const NXRect *)rects :(int)rectCount
{
	return self;
}

- newViewSize
{
	//this is called every time View size changes
	NXRect black = {0, 0, BUFFER_WIDTH, BUFFER_HEIGHT };

	then = now = currentTimeInMs();

	if (oldSize.width == bounds.size.width &&
			oldSize.height == bounds.size.height)
		return self;
	else
	{
		oldSize.width = bounds.size.width;
		oldSize.height = bounds.size.height;
	}
	
	maxCoord.x = bounds.size.width - maxImageSize.width;
	maxCoord.y = bounds.size.height - maxImageSize.height;
	if (maxCoord.x < 0) maxCoord.x = 0;
	if (maxCoord.y < 0) maxCoord.y = 0;


	old.l = old.r = maxCoord.x/2;
	old.b = old.t = maxCoord.y/2;
	imageTo.x = imageTo.y = 0;

	if ([buffer lockFocus])
	{
		PSsetgray(0);
		NXRectFill(&black);
		[buffer unlockFocus];
	}

	return self;
}

- incrementImageNumber
{
	if (now > nextRotationTime)
	{
		if (++currentFrame >= numberOfFrames) currentFrame = 0;
		currentImage = [imageList objectAt:currentFrame];
		[currentImage getSize:&currentImageSize];
		nextRotationTime = now + (850 * secsPerSlide);
	}

	return self;
}

- (const char *)windowTitle
{
	return "SlideShow View by Kamlesh Trivedi";
}

- didLockFocus
{
	return self;
}

- inspector:sender
{
    char buf[MAXPATHLEN];
	
    if (!inspectorView)
	{
		sprintf(buf,"%s/SlideShow.nib",[sender moduleDirectory:"SlideShow"]);
		[NXApp loadNibFile:buf owner:self withNames:NO];
		
		[bounceButton setState:bounce];
		[secsPerSlideSlider setIntValue:secsPerSlide];
		[secsPerSlideTextField setIntValue:secsPerSlide];
		[[pathTextField selectText:self] setStringValue:ssslidesPath];
	}
    return inspectorView;
}

//--------------------------------
- bounceButtonHit:sender
{
	bounce = [sender state];
	
	NXWriteDefault([NXApp appName], DOBOUNCE, (bounce) ? "1" : "0");

	nextRotationTime = now;
	return self;
}

- setSecsPerSlide:sender
{
	char	spsStr[10];

	secsPerSlide = [sender intValue];
	[secsPerSlideTextField takeIntValueFrom:sender];
	
	sprintf(spsStr, "%d", secsPerSlide);
	NXWriteDefault([NXApp appName], SECSPERSLIDE, spsStr);

	nextRotationTime = now;
	return self;
}

- pathButtonHit:sender;
{
	id		openPanel;
	const char*	types[] = { FILEEXTENSION,  (const char*)0 };
	
	openPanel = [OpenPanel new];
	[openPanel setTitle:"Open Slides"];
	[openPanel allowMultipleFiles:NO];
	if ( [openPanel runModalForTypes:types] ) {
		const char* const	*filenames;
	
		for ( filenames = [openPanel filenames]; *filenames; filenames ++ ) {
			char	pathname[MAXPATHLEN];
			
			sprintf(pathname, "%s/%s", [openPanel directory], *filenames);
			[self readSlidesFromFile:pathname];
		}
	}
	nextRotationTime = now;
	return self;
}

- pathSet:sender
{
	const char	*filenames;
	
	filenames = [sender stringValue];
	[self readSlidesFromFile:filenames];
	nextRotationTime = now;
	return self;
}
@end
